#include <gtest/gtest.h>
#include "utility/spw_pair_in.h"
#include "utility/spw_move_in.h"

#include<type_traits>
using namespace spwstd;
using namespace spwstd::spwuti;


class Class{};
class Der: public Class{};

TEST(PairTest, ConstructorTest) {
    // 默认构造函数
    Pair<float, long> a;
    EXPECT_EQ(a.first_, 0);
    using is_sm_float = std::is_same<decltype(a.first_), float>;
    EXPECT_TRUE(is_sm_float::value);

    using is_sm_long = std::is_same<decltype(a.second_), long>;
    EXPECT_TRUE(is_sm_long::value);
    EXPECT_EQ(a.second_, 0);

    // 构造函数
    const float ai = 1;
    const long bi = 1;
    Pair<float, long> b(ai,bi);
    EXPECT_EQ(b.first_, 1.0);
    using is_sm_float = std::is_same<decltype(a.first_), float>;
    EXPECT_TRUE(is_sm_float::value);
    using is_sm_long = std::is_same<decltype(a.second_), long>;
    EXPECT_TRUE(is_sm_long::value);
    EXPECT_EQ(b.second_, 1);

    // 调用Pair(const Pair<U1,U2>& p)
    Pair<double, long> double_long(b);
    EXPECT_TRUE((std::is_same<decltype(double_long.first_), double>::value));
    EXPECT_TRUE((std::is_same<decltype(double_long.second_), long>::value));

    constexpr double dou = 1.0;
    constexpr long lo = 1;
    constexpr Pair<double, long> cx_double_long(dou, lo); 
    constexpr Pair<double, long> p2(1.0, 1);; 
    constexpr long i(999);
    constexpr double j(1.0);
    constexpr Pair<double, long> p3(spwuti::move(p2.first_), i);; 
    constexpr Pair<double, long> p4(j, spwuti::move(p2.second_));; 
    constexpr Pair<double, long> p5(spwuti::move(p2.first_), spwuti::move(p2.second_));; 

    constexpr Pair<char, char> p6{'a', 'b'};
    constexpr Pair<int, int> p7{p6};
    constexpr Pair<int, int> p8(spwuti::move(p6)); // 拷贝
    //
    //使用拷贝构造，
    //因为spwuti::move(p8)的类型是const Pair<int, int>&&
    // 移动构造不接收不可修改值，所以不会匹配到移动构造函数上，
    // 而匹配到拷贝构造函数上
    constexpr Pair<int, int> p9(spwuti::move(p8));
    EXPECT_TRUE((std::is_same<decltype(spwuti::move(p8)), const Pair<int,int>&& >::value));

    constexpr Pair<int, int> p10(spwuti::Pair<int,int>{1,2});//移动

    const Class ac{};
    const Der bc{};
    constexpr Pair<Class, Class> cl{bc, bc}; // 匹配Pair(U&&, U&&)
    constexpr Pair<Class, Class> c2{ac, bc}; // 匹配Pair(const T&, U&&)
    constexpr Pair<Class, Class> c3{bc, ac}; // 匹配Pair(U&&, const T&)
    constexpr Pair<Class, Class> c4{ac, ac}; // 匹配Pair(const T&, const T&)
}

class obj {
    int i;
public:
    obj(int arg = 0): i(arg) { }
    bool operator==(const obj& rhs) const { return i == rhs.i; }
    bool operator<(const obj& rhs) const { return i < rhs.i; }
};

template<typename T>
struct tem
{
    bool b;
public:
    tem(bool arg = 0): b(arg) { }
    bool operator==(const tem& rhs) const { return b == rhs.b; }
    bool operator<(const tem& rhs) const { return int(b) < int(rhs.b); }
};

TEST(PairTest, Test_commom) {
    Pair<bool, long> bool_long_1(true, 433);
    Pair<bool, long> bool_long_2 = spwuti::makePair(true, 433);
    EXPECT_TRUE(bool_long_1 == bool_long_2);
    EXPECT_TRUE(!(bool_long_1 < bool_long_2));

    Pair<const char*, float> cstring_float_1("this is cstring", 433.00f);
    Pair<const char*, float> cstring_float_2 = spwuti::makePair("this is cstring", 433.00f);
    EXPECT_TRUE(cstring_float_1 == cstring_float_2);
    EXPECT_TRUE(!(cstring_float_1 < cstring_float_2));

    Pair<const char*, obj> cstring_obj_1("this is cstring", obj(5));
    Pair<const char*, obj> cstring_obj_2 = spwuti::makePair("this is cstring", obj(5));
    EXPECT_TRUE(cstring_obj_1 == cstring_obj_2);
    EXPECT_TRUE(!(cstring_obj_1 < cstring_obj_2));

    Pair<tem<long>, obj> tem_obj_1(tem<long>(false), obj(5));
    Pair<tem<long>, obj> tem_obj_2 = spwuti::makePair(tem<long>(false), obj(5));
    EXPECT_TRUE(tem_obj_1 == tem_obj_2);
    EXPECT_TRUE(!(tem_obj_1 < tem_obj_2));
}

TEST(PairTest, Test_const) {
    const Pair<bool, long> bool_long_1(true, 433);
    const Pair<bool, long> bool_long_2 = spwuti::makePair(true, 433);
    EXPECT_TRUE(bool_long_1 == bool_long_2);
    EXPECT_TRUE(!(bool_long_1 < bool_long_2));

    const Pair<const char*, float> cstring_float_1("this is cstring", 433.00f);
    const Pair<const char*, float> cstring_float_2 = spwuti::makePair("this is cstring", 433.00f);
    EXPECT_TRUE(cstring_float_1 == cstring_float_2);
    EXPECT_TRUE(!(cstring_float_1 < cstring_float_2));

    const Pair<const char*, obj> cstring_obj_1("this is cstring", obj(5));
    const Pair<const char*, obj> cstring_obj_2 = spwuti::makePair("this is cstring", obj(5));
    EXPECT_TRUE(cstring_obj_1 == cstring_obj_2);
    EXPECT_TRUE(!(cstring_obj_1 < cstring_obj_2));

    const Pair<tem<long>, obj> tem_obj_1(tem<long>(false), obj(5));
    const Pair<tem<long>, obj> tem_obj_2 = spwuti::makePair(tem<long>(false), obj(5));
    EXPECT_TRUE(tem_obj_1 == tem_obj_2);
    EXPECT_TRUE(!(tem_obj_1 < tem_obj_2));
}

// const&
TEST(PairTest, Test_const_refer) {
    const obj& obj1 = obj(5);
    const spwuti::Pair<const char*, obj> cstring_obj_1("this is const_refer", obj1);
    const spwuti::Pair<const char*, obj> cstring_obj_2 = spwuti::makePair("this is const_refer", obj1);
    EXPECT_TRUE(cstring_obj_1 == cstring_obj_2);
    EXPECT_TRUE(!(cstring_obj_1 < cstring_obj_2));

    const tem<long>& temp1 = tem<long>(false);
    const Pair<tem<long>, obj> tem_obj_1(temp1, obj1);
    const Pair<tem<long>, obj> tem_obj_2 = spwuti::makePair(temp1, obj1);
    EXPECT_TRUE(tem_obj_1 == tem_obj_2);
    EXPECT_TRUE(!(tem_obj_1 < tem_obj_2));
}

TEST(PairTest, Test_bool_bool) {
    Pair<bool, bool> bool_bool_1(true, false);
    Pair<bool, bool> bool_bool_2 = spwuti::makePair(true, false);
    EXPECT_TRUE(bool_bool_1 == bool_bool_2);
    EXPECT_TRUE(!(bool_bool_1 < bool_bool_2));
}

// 移动操作检测
struct X{
    explicit X(int, int) {}
    X(const X&) = delete;
};

struct move_only{
    move_only(){}
    move_only(move_only&&){}
    move_only(const move_only&) = delete;
};

TEST(PairTest, Test_move) {
  int *ip = 0;
  int X::*mp = 0;

  spwuti::Pair<int*, int*> p1(0, 0);
  spwuti::Pair<int*, int*> p2(ip, 0);
  spwuti::Pair<int*, int*> p3(0, ip);
  spwuti::Pair<int*, int*> p4(ip, ip);

  spwuti::Pair<int X::*, int*> p5(0, 0);
  spwuti::Pair<int X::*, int X::*> p6(mp, 0);
  spwuti::Pair<int X::*, int X::*> p7(0, mp);
  spwuti::Pair<int X::*, int X::*> p8(mp, mp);

  spwuti::Pair<int*, move_only> p9(0, move_only());
  spwuti::Pair<int X::*, move_only> p10(0, move_only());
  spwuti::Pair<move_only, int*> p11(move_only(), 0);
  spwuti::Pair<move_only, int X::*> p12(move_only(), 0);

  spwuti::Pair<int*, move_only> p13(ip, move_only());
  spwuti::Pair<int X::*, move_only> p14(mp, move_only());
  spwuti::Pair<move_only, int*> p15(move_only(), ip);
  spwuti::Pair<move_only, int X::*> p16(move_only(), mp);

  spwuti::Pair<move_only, move_only> p17{move_only(), move_only()};
}


TEST(PairTest, OtherTest){
// 逻辑运算符
    spwuti::Pair<int, double> base{1,1.0};
    spwuti::Pair<int, double> e{1,1.0};
    spwuti::Pair<int, double> g{1,0.9};
    spwuti::Pair<int, double> l{1,1.1};

    // std::pair<int, double> base1{1,1.0};
    // std::pair<int, const double> e1{1,1.0};
    EXPECT_TRUE(base == e);
    EXPECT_TRUE(base >= e);
    EXPECT_TRUE(base > g);
    EXPECT_TRUE(base >= g);
    EXPECT_TRUE(base <= e);
    EXPECT_TRUE(base <= l);
    EXPECT_TRUE(base != l);

// swap
    base.Swap(g);
    EXPECT_EQ(base.second_, 0.9);
    spwuti::swap(base, g);
    EXPECT_EQ(base.second_, 1.0);
}
